﻿/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2017 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "Gpio.h"
#include "File.h"
#include "Utils.h"

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/* 定义平台 */		
//#define PLATFORM_TQ210  	
#define PLATFORM_IMX6


/**
 *  \class  GpioGroup_S
 *  \brief  GPIO组结构体 
 */
typedef struct{
	std::string m_groupLable;/**< Gpio的组标 */
	int m_baseAddr;       /**< Gpio的偏移地址 */
}GpioGroup_S;

#if defined(PLATFORM_TQ210)
#define GPIO_GROUP_MAX	27
#elif defined(PLATFORM_IMX6)
#define GPIO_GROUP_MAX	7
#endif

static GpioGroup_S GpioGroupInfo[GPIO_GROUP_MAX]={
#if defined(PLATFORM_TQ210)
	{"GPA0",0},		/* 1 */
	{"GPA1",9},		/* 2 */
	{"GPB",14},		/* 3 */
	{"GPC0",23},	/* 4 */
	{"GPC1",29},	/* 5 */
	{"GPD0",35},	/* 6 */
	{"GPD1",40},	/* 7 */
	{"GPE0",47},	/* 8 */
	{"GPE1",56},	/* 9 */
	{"GPF0",62},	/* 10 */
	{"GPF1",71},	/* 11 */
	{"GPF2",80},	/* 12 */
	{"GPF3",89},	/* 13 */
	{"GPG0",96},	/* 14 */
	{"GPG1",104},	/* 15 */
	{"GPG2",112},	/* 16 */
	{"GPG3",120},	/* 17 */
	{"GPH0",128},	/* 18 */
	{"GPH1",137},	/* 19 */
	{"GPH2",146},	/* 20 */
	{"GPH3",155},	/* 21 */
	{"GPI",164},	/* 22 */
	{"GPJ0",172},	/* 23 */
	{"GPJ1",181},	/* 24 */
	{"GPJ2",188},	/* 25 */
	{"GPJ3",197},	/* 26 */
	{"GPJ4",206},	/* 27 */
#elif defined(PLATFORM_IMX6)
    {"GPIO1",0},	/* 1 */
	{"GPIO2",32},	/* 2 */ 
	{"GPIO3",64},	/* 3 */
	{"GPIO4",96},	/* 4 */ 
	{"GPIO5",128},	/* 5 */
	{"GPIO6",160},	/* 6 */ 
    {"GPIO7",192},	/* 7 */
#endif
}; 

/**
 *  \brief  GPIO构造函数
 *  \param  fd GPIO对应的pin号
 *  \return none
 *  \note   none
 */
GpioImp::GpioImp(int fd):
m_fd(fd)
{
	char buf[6]={0};
	sprintf(buf,"%d",m_fd);
	m_gpioDir="/sys/class/gpio/gpio";
	m_gpioDir += buf;
	m_gpioDirectionFile = m_gpioDir+"/direction";
	m_gpioValueFile = m_gpioDir+"/value";
	m_gpioExportFile = "/sys/class/gpio/export";
}
GpioImp::~GpioImp()
{
}

/**
 *  \brief  配置GPIO
 *  \param  direction GPIO方向: GPIO::IN或GPIO::OUT
 *  \param  value GPIO值: GPIO::LOW或GPIO::HIGN
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int GpioImp::config(int direction,int value)
{
	int i,ret,len,tmpFd;
	
	/* 导出gpio */
	if(!File::isExist(m_gpioDir.c_str()))
	{
		tmpFd = ::open(m_gpioExportFile.c_str(),O_WRONLY);
		if(-1==tmpFd)
		{
			TRACE_ERR_CLASS("open gpio(%d) export file error(%s).\n",m_fd,ERROR_STRING);
			return STATUS_ERROR;
		}
		
		char buf[6]={0};
		sprintf(buf,"%d",m_fd);
		len = strlen(buf);
		ret = ::write(tmpFd, buf, len);
		if (ret!=len)
		{
			TRACE_ERR_CLASS("write gpio(%d) export file failed,ret=%d,len=%d\n",m_fd,ret,len);
			::close(tmpFd);
			return STATUS_ERROR;
		}
		else
		{
			if(!File::isExist(m_gpioDir.c_str()))
			{
				TRACE_ERR_CLASS("can't export gpio(%d)\n",m_fd);
				::close(tmpFd);
				return STATUS_ERROR;
			}
		}
		::close(tmpFd);
	}

	i=0;
	while(1)
	{
		if (File::isExist(m_gpioDirectionFile.c_str()))
		{
			break;
		}
		if(i>5)
		{
			return STATUS_ERROR;
		}
		sleep(1);
	}

	/* 设置gpio属性 */
	tmpFd = ::open(m_gpioDirectionFile.c_str(),O_WRONLY);
	if(-1==tmpFd)
	{
		TRACE_ERR_CLASS("open gpio(%d) direction file(%s) error(%s).\n",m_fd,m_gpioDirectionFile.c_str(),ERROR_STRING);
		return STATUS_ERROR;
	}
	if(GPIO::IN==direction)
	{
		len = 2;
		ret = ::write(tmpFd, "in", len);
	}
	else
	{
		len =3;
		ret = ::write(tmpFd, "out", len);
	}
	
	if (ret!=len)
	{
		TRACE_ERR_CLASS("gpio(%d)set direction error,ret=%d,len=%d\n",m_fd,ret,len);
		::close(tmpFd);
		return STATUS_ERROR;
	}
	::close(tmpFd);

	if (GPIO::OUT==direction)
	{
	    setPinValue(value);
	}
	return STATUS_OK;
}

/**
 *  \brief  设置GPIO Pin输出电平
 *  \param  value GPIO值: GPIO::LOW或GPIO::HIGN
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int GpioImp::setPinValue(int value)
{
	int tmpFd,ret;
	if(!File::isExist(m_gpioDir.c_str()))
	{
		TRACE_ERR_CLASS("gpio(%d) not config.\n",m_fd);
		return STATUS_ERROR;
	}
	tmpFd = ::open(m_gpioValueFile.c_str(),O_RDWR);
	if(-1==tmpFd)
	{
		TRACE_ERR_CLASS("open gpio(%d) value file(%s) error(%s).\n",m_fd,m_gpioValueFile.c_str(),ERROR_STRING);
		return STATUS_ERROR;
	}
	if(GPIO::LOW==value)
	{
		ret = ::write(tmpFd, "0", 1);
	}
	else
	{
		ret = ::write(tmpFd, "1", 1);
	}
	if (ret!=1)
	{
		TRACE_ERR_CLASS("gpio(%d) set value error(%s),ret=%d\n",m_fd,ERROR_STRING,ret);
		::close(tmpFd);
		return STATUS_ERROR;
	}
	::close(tmpFd);
	return STATUS_OK;
}

/**
 *  \brief  读取GPIO Pin输入电平
 *  \param  void
 *  \return 成功返回pin值,失败返回STATUS_ERROR
 *  \note   none
 */
int GpioImp::getPinValue()
{
	int tmpFd,ret;
	if(!File::isExist(m_gpioDir.c_str()))
	{
		TRACE_ERR_CLASS("gpio(%d) not config.\n",m_fd);
		return STATUS_ERROR;
	}
	
	tmpFd = ::open(m_gpioValueFile.c_str(),O_RDWR);
	if(-1==tmpFd)
	{
		TRACE_ERR_CLASS("open gpio(%d) direction file(%s) error(%s).\n",m_fd,m_gpioValueFile.c_str(),ERROR_STRING);
		return STATUS_ERROR;
	}
	char value;
	ret = ::read(tmpFd, &value, 1);
	if (ret!=1)
	{
		TRACE_ERR_CLASS("gpio(%d) get value error(%s),ret=%d\n",m_fd,ERROR_STRING,ret);
		::close(tmpFd);
		return STATUS_ERROR;
	}
	::close(tmpFd);
	if(value=='0')
	{
		return GPIO::LOW;	
	}
	else
	{
		return GPIO::HIGH;
	}
}
/**
 *  \brief  GPIO工厂获取GPIO实例
 *  \param  groupName GPIO名称字符串
 *  \return 成功返回GPIO实例GpioImp*,失败返回NULL
 *  \note   groupName格式:"group:pin",如"GPIO0:2"表示第0组,第2引脚.
 */
GpioImp* GpioFactory::getGpioImp(std::string gpioName)
{
    std::string tmp="\r"+gpioName+"\n";
    std::vector<std::string> strVect = Utils::cutString(tmp, "\r", "\n", ":");
    if (strVect.size()!=2)
    {
        TRACE_ERR_CLASS("gpio[%s] not exist!\n",gpioName.c_str());
        return NULL;
    }
    return getGpioImp(strVect[0],atoi(strVect[1].c_str()));
}
/**
 *  \brief  GPIO工厂获取GPIO实例
 *  \param  group GPIO所在组
 *  \param  pin GPIO pin脚
 *  \return 成功返回GPIO实例GpioImp*,失败返回NULL
 *  \note   none
 */
GpioImp* GpioFactory::getGpioImp(std::string group, int pin)
{
	int i;
	int gpioNum=-1;
	GpioImp* gpio=NULL;
	for(i=0; i<GPIO_GROUP_MAX; i++)
	{
		if(group==GpioGroupInfo[i].m_groupLable)
		{
			gpioNum = GpioGroupInfo[i].m_baseAddr+pin;
			break;
		}
	}
	if(-1==gpioNum)
	{
		TRACE_ERR_CLASS("Can't support GPIO(%s:%d).\n",group.c_str(),pin);
		return NULL;
	}
	GpioImpMap::iterator iter=m_gpioImpTable.find(gpioNum);
	if(iter!=m_gpioImpTable.end())
	{
		//TRACE_DBG_CLASS("get Gpio(%d)[%s:%d] ok.\n",gpioNum,group.c_str(),pin);
		return iter->second;
	}
	gpio = NEW_OBJ GpioImp(gpioNum);

	m_gpioImpTable.insert(std::make_pair<int, GpioImp*>(gpioNum, gpio));
	//TRACE_DBG_CLASS("get Gpio(%d)[%s:%d] ok.\n",gpioNum,group.c_str(),pin);
	return gpio;
}
